package net.fornwall.eclipsecoder.actions;

import java.io.Closeable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

import net.fornwall.eclipsecoder.preferences.EclipseCoderPlugin;
import net.fornwall.eclipsecoder.util.Utilities;

import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.osgi.framework.Bundle;

/**
 * Action to download the TopCoder contest applet jar file from
 * http://www.topcoder.com/contest/classes/ContestApplet.jar.
 */
public class DownloadAppletJob extends Job {

	private static final String APPLET_URL = "http://www.topcoder.com/contest/classes/ContestApplet.jar";

	private static final int ESTIMATED_APPLET_SIZE = 1800000;

	public static void runOrDownload() {
		File localAppletJar = getLocalAppletJar();
		if (localAppletJar.exists()) {
			try {
				long lastModifiedLocal = localAppletJar.lastModified();
				long lastModifiedRemote = new URL(APPLET_URL).openConnection()
						.getLastModified();
				if (lastModifiedRemote > lastModifiedLocal) {
					DownloadAppletJob job = new DownloadAppletJob(
							"Updating TopCoder contest applet");
					job.setUser(true);
					job.schedule();
					return;
				}
				URL appletURL = getLocalAppletJar().toURI().toURL();
				Bundle bundle = EclipseCoderPlugin.getDefault().getBundle();
				Path path = new Path("arenaplugin.jar");
				URL pluginURL = FileLocator.find(bundle, path, null);

				ClassLoader parent = Thread.currentThread()
						.getContextClassLoader();
				ClassLoader loader = new URLClassLoader(new URL[] { appletURL,
						pluginURL }, parent);

				if (classLoaderWorks(loader)) {
					StartActionImpl.run(loader);
				} else {
					StartAction.getAction().setEnabled(true);
					if (getLocalAppletJar().delete()) {
						Utilities
								.showMessageDialog(
										"Error starting applet",
										"The downloaded applet jar could not be used and has been deleted. Try to start again.",
										true);
						return;
					}
					Utilities
							.showMessageDialog(
									"Error starting applet",
									"The downloaded applet jar could not be used and cannot be removed - please check the file "
											+ getLocalAppletJar()
													.getAbsolutePath(), true);
					return;
				}

			} catch (Exception e) {
				Utilities.showException(e);
				StartAction.getAction().setEnabled(true);
			}

			return;
		}

		DownloadAppletJob job = new DownloadAppletJob(
				"Downloading TopCoder contest applet");
		job.setUser(true);
		job.schedule();
	}

	private static boolean classLoaderWorks(ClassLoader loader) {
		try {
			loader
					.loadClass("com.topcoder.client.contestApplet.runner.generic");
			return true;
		} catch (Exception e) {
			return false;
		}
	}

	private static File getLocalAppletJar() {
		File userHome = new File(System.getProperty("user.home"));
		File eclipsecoderDir = new File(userHome, ".eclipsecoder");
		if (((eclipsecoderDir.exists() && eclipsecoderDir.isDirectory()) || eclipsecoderDir
				.mkdir())
				&& (eclipsecoderDir.canRead() && eclipsecoderDir.canWrite())) {
			// try with global jar location outside workspace
			return new File(eclipsecoderDir, "ContestApplet.jar");
		}

		IPath stateLocation = EclipseCoderPlugin.getDefault()
				.getStateLocation();
		IPath jarLocation = stateLocation.append("ContestApplet.jar");
		return jarLocation.toFile();
	}

	public DownloadAppletJob(String name) {
		super(name);
	}

	@Override
	protected IStatus run(IProgressMonitor monitor) {
		monitor.beginTask(getName(), ESTIMATED_APPLET_SIZE);
		InputStream in = null;
		OutputStream out = null;

		try {
			in = new URL(APPLET_URL).openStream();
			out = new FileOutputStream(getLocalAppletJar());
			byte[] buffer = new byte[4096];
			int i;
			while ((i = in.read(buffer)) != -1) {
				if (monitor.isCanceled()) {
					Utilities.close(out);
					if (getLocalAppletJar().exists()) {
						getLocalAppletJar().delete();
					}
					StartAction.getAction().setEnabled(true);
					return Status.CANCEL_STATUS;
				}
				out.write(buffer, 0, i);
				monitor.worked(i);
			}
		} catch (MalformedURLException e) {
			// should never happen as APPLET_URL is a valid URL
			Utilities.close(out);
			if (getLocalAppletJar().exists()) {
				getLocalAppletJar().delete();
			}
			StartAction.getAction().setEnabled(true);
			Utilities.showException(e);
		} catch (IOException e) {
			Utilities.close(out);
			if (getLocalAppletJar().exists()) {
				getLocalAppletJar().delete();
			}
			StartAction.getAction().setEnabled(true);
			return new Status(IStatus.ERROR, EclipseCoderPlugin.PLUGIN_ID,
					IStatus.OK, e.getMessage(), e);
		} finally {
			close(in);
			close(out);
		}

		DownloadAppletJob.runOrDownload();
		return Status.OK_STATUS;
	}

	private static final void close(Closeable stream) {
		if (stream != null) {
			try {
				stream.close();
			} catch (IOException e) {
				Utilities.showException(e);
			}
		}
	}
}
